﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Controls;
using System.Diagnostics;
using System.Threading;
using Silvermoon.Animations;

namespace Silvermoon.Core
{
    /// <summary>
    /// Handles time events both for refreshing the graphics and performing asynchronous and synchronous animations/transitions in a synchronized way.
    /// </summary>
    internal class TimeEventServer : IDisposable
    {
        /// <summary>
        /// Specifies the refresh interval in ms.
        /// </summary>
        const int RefreshInterval = 1000 / 25;
        #region fields

        private System.Windows.Forms.Timer timer;
        private Screen host;
        private TimeEventArgs timeEventArgs;
        private int lastTick;

#if ANALYSIS
        const int tickstacksize = 25;
        int[] ticks = new int[tickstacksize];
        int tickstackptr = 0;
        int ticksum = 0;
        int lasttick;
#endif

        #endregion
        #region IDisposable Members


        public TimeEventServer(Screen host)
            : base()
        {
            this.host = host;
            timeEventArgs = new TimeEventArgs();

            timer = new System.Windows.Forms.Timer();
            timer.Tick += new EventHandler(OnEventTick);
            timer.Interval = RefreshInterval;

        }

        public void Dispose()
        {
            if (timer != null)
            {
                timer.Tick -= OnEventTick;
                timer.Enabled = false;
                timer.Dispose();
                timer = null;
            }
        }

        #endregion
        #region props

#if ANALYSIS
        public float TicksPerFrame { get { return ((float)ticksum) / ((float)tickstacksize); } }
#else
        public float TicksPerFrame { get { return 0; } }
#endif

        public bool Enabled { get { return timer != null ? timer.Enabled : false; } set { timer.Enabled = value; } }

        #endregion
        #region methods

        void OnEventTick(object sender, EventArgs e)
        {
            Progress();
        }

        public void Progress()
        {
            try
            {
                int tick = Environment.TickCount;
                int dt = tick - lastTick;
                if (dt >= RefreshInterval)
                {
                    PerformAnalysis(tick);
                    lastTick = tick;

                    InvokeThreadDelegates();

                    if (Tick != null)
                    {
                        timeEventArgs.SetTick(tick);
                        Tick(this, timeEventArgs);
                    }

                    if (!host.IsRendering && Enabled && host.Renderer.Invalidated)
                    {
                        host.Update();
                    }
                    InvokeDelegates();
                }
            }
            catch (Exception ex)
            {
                if (Debugger.IsAttached) throw;
                Screen s = Screen.Current;
                if (s != null) s.RaiseUnhandledException(ex);
            }
        }

        /// <summary>
        /// Invoke all attached delegates from different threads and remove:
        /// </summary>
        private void InvokeThreadDelegates()
        {
            var invokes = Invokes;
            if (invokes != null)
            {
                lock (this)
                {
                    invokes = Invokes;
                    Invokes = null;
                    if (invokes != null) invokes();
                }
            }
        }

        /// <summary>
        /// Invoke all attached delegates from different threads and remove:
        /// </summary>
        private void InvokeDelegates()
        {
            var invokes = UnthreadedInvokes;
            if (invokes != null)
            {
                UnthreadedInvokes = null;
                if (invokes != null) invokes();
            }
        }

        /// <summary>
        /// Only compiles when ANALYSIS is defined in project properties  under Build/Conditional compilation properties (separated with comma).
        /// </summary>
        [Conditional("ANALYSIS")]
        private void PerformAnalysis(int tick)
        {
#if ANALYSIS
            int dt = tick - lasttick;
            lasttick = tick;

            int tick0 = tickstackptr + 1;
            if (tick0 >= tickstacksize) tick0 = 0;
            ticksum += dt - ticks[tick0];
            ticks[tick0] = dt;
            tickstackptr = tick0;
#endif
        }

        public int NumberOfTickInvocations
        {
            get
            {
                if (Tick == null) return 0;
                var list = Tick.GetInvocationList();
                //foreach (var o in list)
                //{
                //    Animation a = o.Target as Animation;
                //    if (a != null)
                //    {
                //        Trace.WriteLine(a.IsCompleted);
                //    }
                //}
                return list != null ? list.Length : 0;
            }
        }

        #endregion
        #region events

        public event EventHandler<TimeEventArgs> Tick;
        public event ThreadStart Invokes;
        public event ThreadStart UnthreadedInvokes;


        #endregion
    }

    #region TimeEventArgs

    public class TimeEventArgs : EventArgs
    {
        /// <summary>
        /// Gets the current system tick in milliseconds
        /// </summary>
        public int Tick { get; private set; }

        internal TimeEventArgs()
            : base()
        {
        }

        internal void SetTick(int value)
        {
            Tick = value;
        }
    }

    #endregion

}
